home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 1 / PC Actual CD 01.iso / trucos / utils / util.wri < prev   
Encoding:
Text File  |  1995-02-10  |  38.5 KB  |  2,191 lines

  1.  
  2.  
  3. UTILIDADES
  4.  
  5. CARGADOR DE EJECUTABLES
  6.  
  7. Esta rutina no es mßs que un cargador/reubicador de ficheros EXE. Los
  8. creadores del DOS no debieron pensar en su dφa que en ciertas
  9. ocasiones la carga de ficheros EXE mediante funciones del sistema no
  10. se ajusta a las necesidades de ciertas aplicaciones, como por ejemplo
  11. aquellas que requieran cargar un fichero EXE desde dentro de otro
  12. fichero.
  13.  
  14. Esta rutina subsana este problema, ya que permite cargar ficheros y
  15. especificar en que offset dentro de Θstos se encuentra el EXE (para
  16. una carga normal de ficheros EXE se usa un offset de 0 y ya estß). No
  17. puede cargar ficheros con overlays ni ficheros COM, y aunque no
  18. funciona en el 100% de los casos, su efectividad es bastante alta.
  19.  
  20. Las rutinas son para Turbo Pascal 7.0. Se compila primero la unidad
  21. ½Exec╗ y despuΘs el fichero ½Exectest╗. Su uso es sencillo:
  22.  
  23. EXECTEST fichero.exe
  24.  
  25. La unidad ½exec╗ es la que sigue:
  26.  
  27. UNIT Exec;
  28.  
  29. INTERFACE
  30.  
  31. USES
  32.  
  33.  Dos;
  34.  
  35. VAR
  36.  
  37.  FicheroExe : STRING;
  38.  
  39.  header: ARRAY [1..28] OF BYTE;
  40.  
  41.  handle,PSPSegment,StartSegment:WORD;
  42.  
  43.  item: ARRAY [1..2] OF WORD;
  44.  
  45.  memory,imagesize:WORD;
  46.  
  47. PROCEDURE ExecFile(pos:LONGINT);
  48.  
  49.  
  50. IMPLEMENTATION
  51.  
  52. { convierte un string en formato ASCIIZ para utilizarlo con }
  53.  
  54. { las funciones del DOS }
  55.  
  56. PROCEDURE StringToAsciiz; ASSEMBLER;
  57.  
  58. ASM
  59.  
  60.  mov bx,OFFSET FicheroExe
  61.  
  62.  xor cx,cx
  63.  
  64.  mov cl,[bx]
  65.  
  66. @@StringToASCII:
  67.  
  68.  mov al,[bx+1]
  69.  
  70.  mov [bx],al
  71.  
  72.  inc bx
  73.  
  74.  loop @@StringToASCII
  75.  
  76.  mov byte ptr [bx],0
  77.  
  78. END;
  79.  
  80. { Carga, reubica y ejecuta un fichero .EXE }
  81.  
  82. PROCEDURE ExecFile(pos:LONGINT); ASSEMBLER;
  83.  
  84. ASM
  85.  
  86.  push es
  87.  
  88.  call StringToAsciiz { convierte el nombre del .exe a ASCIIZ }
  89.  
  90.  push ds
  91.  
  92.  mov ax,3d00h
  93.  
  94.  mov dx,OFFSET FicheroExe
  95.  
  96.  int 21h { abre el fichero para s≤lo lectura }
  97.  
  98.  { indexado del fichero }
  99.  
  100.  push ax
  101.  
  102.  mov bx,ax { handle del fichero }
  103.  
  104.  mov ax,04200h
  105.  
  106.  mov dx,word ptr [pos]
  107.  
  108.  mov cx,word ptr [pos+2]
  109.  
  110.  int 21h
  111.  
  112.  pop ax
  113.  
  114.  mov [handle],ax { guarda el handle del fichero }
  115.  
  116.  mov bx,[handle]
  117.  
  118.  mov dx,OFFSET header { cabecera del fichero .exe }
  119.  
  120.  mov cx,28
  121.  
  122.  mov ah,3fh
  123.  
  124.  int 21h { lee los 28 bytes estßndar de la cabecera }
  125.  
  126.  pop ds
  127.  
  128.  mov bx,OFFSET header
  129.  
  130.  mov cx,[bx+08h] { longitud del header en parßgrafos }
  131.  
  132.  mov ax,16
  133.  
  134.  imul cx { DX:AX -> posici≤n de inicio de m≤dulo }
  135.  
  136.  mov cx,dx
  137.  
  138.  mov dx,ax { CX:DX -> posici≤n para el LSEEK }
  139.  
  140.  add dx,word ptr [pos]
  141.  
  142.  adc cx,word ptr [pos+2]
  143.  
  144.  mov bx,[handle]
  145.  
  146.  xor al,al
  147.  
  148.  mov ah,42h
  149.  
  150.  int 21h
  151.  
  152. { posiciona el puntero del fichero al inicio del m≤dulo del programa }
  153.  
  154.  mov bx,OFFSET header
  155.  
  156.  mov ax,[bx+4] { longitud total incluyendo la cabecera }
  157.  
  158.  inc ax { 1+ por si acaso }
  159.  
  160.  mov cx,32
  161.  
  162.  mul cx { parßgrafos totales }
  163.  
  164.  mov [imagesize],ax { tama±o del modulo del programa }
  165.  
  166.  mov bx,0ffffh
  167.  
  168.  mov ah,48h
  169.  
  170.  int 21h
  171.  
  172.  jnc @@no_error0
  173.  
  174.  mov [memory],bx
  175.  
  176.  mov ah,48h
  177.  
  178.  int 21h { con esto reservamos el mßximo de memoria }
  179.  
  180. @no_error0:
  181.  
  182.  mov [PSPSegment],ax
  183.  
  184.  { genera un nuevo PSP...UNDOCUMENTED! }
  185.  
  186.  push ax
  187.  
  188.  mov dx,ax
  189.  
  190.  mov ah,055h
  191.  
  192.  mov si,dx
  193.  
  194.  add si,10h
  195.  
  196.  add si,[memory]
  197.  
  198.  { valor a escribir en el campo de memoria del PSP }
  199.  
  200.  int 21h
  201.  
  202.  pop ax
  203.  
  204.  mov es,ax
  205.  
  206.  xor di,di
  207.  
  208.  mov word ptr es:[di+0ah],OFFSET @@retorna
  209.  
  210.  mov es:[di+0ch],cs
  211.  
  212.  add ax,16
  213.  
  214.  mov [StartSegment],ax
  215.  
  216.  { aquφ nos encargamos de leer el m≤dulo principal del programa, que }
  217.  
  218.  { no es mßs que el c≤digo y los datos en sφ... }
  219.  
  220.  mov bx,[handle]
  221.  
  222.  mov cx,[imagesize]
  223.  
  224.  push ds
  225.  
  226.  mov ds,[StartSegment]
  227.  
  228. @@next_block:
  229.  
  230.  push cx
  231.  
  232.  xor dx,dx
  233.  
  234.  mov cx,16 { 16 bytes cada bloque }
  235.  
  236.  mov ah,3fh
  237.  
  238.  xor al,al
  239.  
  240.  int 21h
  241.  
  242.  jc @@END_read
  243.  
  244.  mov ax,ds
  245.  
  246.  inc ax
  247.  
  248.  mov ds,ax
  249.  
  250.  pop cx
  251.  
  252.  loop @@next_block
  253.  
  254.  pop ds
  255.  
  256.  jmp @@readgood
  257.  
  258. @@END_read:
  259.  
  260.  pop cx
  261.  
  262.  pop ds
  263.  
  264. @@readgood:
  265.  
  266.  { ahora posiciona el puntero del fichero al inicio de la tabla de }
  267.  
  268.  { reubicaci≤n del fichero .EXE }
  269.  
  270.  mov si,OFFSET header
  271.  
  272.  mov ax,[si+18h]
  273.  
  274.  { offset en bytes en el fichero de la tabla de reubicaci≤n del .exe }
  275.  
  276.  mov dx,ax
  277.  
  278.  xor cx,cx
  279.  
  280.  add dx,word ptr [pos] { mßs la posici≤n }
  281.  
  282.  adc cx,word ptr [pos+2]
  283.  
  284.  mov bx,[handle]
  285.  
  286.  mov ah,42h
  287.  
  288.  xor al,al
  289.  
  290.  int 21h
  291.  
  292.  { ahora comienza la reubicaci≤n de verdad del fichero }
  293.  
  294.  mov si,OFFSET header
  295.  
  296.  mov cx,[si+06h] { n·mero de datos de items de la tabla de reubicaci≤n }
  297.  
  298.  test cx,0ffffh
  299.  
  300.  jz @@no_realocate
  301.  
  302. @@siguiente_item:
  303.  
  304.  push cx
  305.  
  306.  mov dx,OFFSET item
  307.  
  308.  mov bx,[handle]
  309.  
  310.  mov cx,4 { cada item se compone de offset:segmento -> 4 bytes }
  311.  
  312.  mov ah,3fh
  313.  
  314.  int 21h
  315.  
  316.  mov bx,OFFSET item
  317.  
  318.  mov ax,[StartSegment]
  319.  
  320.  { a cada posici≤n indicada por el item se
  321.  
  322.  le suma el segmento de inicio del m≤dulo }
  323.  
  324.  add ax,[bx+2]
  325.  
  326.  mov di,[bx]
  327.  
  328.  mov es,ax
  329.  
  330.  mov ax,es:[di]
  331.  
  332.  add ax,[StartSegment]
  333.  
  334.  mov es:[di],ax
  335.  
  336.  pop cx
  337.  
  338.  loop @@siguiente_item
  339.  
  340. @@no_realocate:
  341.  
  342.  mov bx,[handle]
  343.  
  344.  mov ah,3eh
  345.  
  346.  int 21h { cierra el fichero }
  347.  
  348.  { set new PSP address...UNDOCUMENTED! }
  349.  
  350.  mov bx,[PSPSegment]
  351.  
  352.  mov ah,50h
  353.  
  354.  int 21h
  355.  
  356.  mov bx,OFFSET @@oldss
  357.  
  358.  cli
  359.  
  360.  mov cs:[bx],ss
  361.  
  362.  mov cs:[bx+2],sp
  363.  
  364.  mov cs:[bx+4],bp
  365.  
  366.  sti
  367.  
  368.  mov bx,OFFSET header
  369.  
  370.  mov ax,[bx+0eh]
  371.  
  372.  add ax,[StartSegment]
  373.  
  374.  cli
  375.  
  376.  mov ss,ax
  377.  
  378.  mov sp,[bx+10h] { genera SS:SP a partir del header }
  379.  
  380.  sti
  381.  
  382.  mov ax,[bx+14h]
  383.  
  384.  mov dx,[bx+16h]
  385.  
  386.  add dx,[StartSegment] { DX:AX -> CS:IP }
  387.  
  388.  push dx
  389.  
  390.  push ax
  391.  
  392.  mov es,[PSPSegment]
  393.  
  394.  mov ds,[PSPSegment]
  395.  
  396.  xor ax,ax { borra todos los registros }
  397.  
  398.  mov di,ax
  399.  
  400.  mov si,ax
  401.  
  402.  mov bx,ax
  403.  
  404.  mov cx,ax
  405.  
  406.  mov dx,ax
  407.  
  408.  mov bp,ax
  409.  
  410.  retf
  411.  
  412. @@retorna:
  413.  
  414.  mov bx,OFFSET @@oldss
  415.  
  416.  cli
  417.  
  418.  mov ss,cs:[bx] { recupera todos los registros }
  419.  
  420.  mov sp,cs:[bx+2]
  421.  
  422.  mov bp,cs:[bx+4]
  423.  
  424.  sti
  425.  
  426.  mov ax,SEG @data
  427.  
  428.  mov ds,ax
  429.  
  430.  mov es,[PSPSegment]
  431.  
  432.  mov ah,49h
  433.  
  434.  int 21h { libera la memoria guardada por el }
  435.  
  436.  jmp @@restore
  437.  
  438. @@oldss: dw 0
  439.  
  440. @@oldsp: dw 0
  441.  
  442. @@oldbp: dw 0
  443.  
  444. @@restore:
  445.  
  446.  pop es
  447.  
  448. END;
  449.  
  450. BEGIN
  451.  
  452. END.
  453.  
  454.  
  455. El programa ½Exectest╗ es el sigiente:
  456.  
  457.  
  458. {$M,16000,0,0}
  459.  
  460. {$F+}
  461.  
  462. USES Dos, Crt, Exec;
  463.  
  464. { devuelve la memoria que queda libre }
  465.  
  466. FUNCTION MemoriaLibre: WORD; ASSEMBLER;
  467.  
  468. ASM
  469.  
  470.  mov bx,0ffffh
  471.  
  472.  mov ah,48h
  473.  
  474.  int 21h
  475.  
  476.  mov ax,bx
  477.  
  478. END;
  479.  
  480. { programa principal }
  481.  
  482. BEGIN
  483.  
  484.  WriteLn('Exec loader version 2.11');
  485.  
  486.  IF paramcount=0 THEN
  487.  
  488.  BEGIN
  489.  
  490.  WriteLn('Usage <filename.exe>');
  491.  
  492.  Halt;
  493.  
  494.  END;
  495.  
  496.  Write('Memoria libre antes de ejecutar el programa:
  497.  ',LongInt(MemoriaLibre)*16,' bytes');
  498.  
  499.  FicheroExe:=paramstr(1);
  500.  
  501.  ExecFile(0);
  502.  
  503.  ASM
  504.  
  505.  mov ax,03
  506.  
  507.  int $10
  508.  
  509.  END;
  510.  
  511.  Write('Memoria libre despuΘs de salir del programa:
  512.  ',LongInt(MemoriaLibre)*16,' bytes');
  513.  
  514. END.
  515.  
  516. JosΘ Cabezas Garcφa
  517.  
  518. Barcelona
  519.  
  520.  
  521. ACTUALIZACION DEL PATH
  522.  
  523. El siguiente programa tiene como finalidad el a±adir o actualizar
  524. directorios en la variable de entorno ½Path╗ de forma temporal, es
  525. decir, hasta que se apague el ordenador, o bien de forma definitiva
  526. a±adiΘndolo automßticamente en el ½autoexec.bat╗.
  527.  
  528. Para a±adir directorios de forma temporal basta con pasarle como
  529. parßmetros los directorios a a±adir separados por espacios o por punto
  530. y coma (;). Si queremos que los cambios sean permanentes, esto es,
  531. que se actualice el ½autoexec.bat╗, basta con poner el modificador /A
  532. al final del comando, justo despuΘs de los nuevos directorios que
  533. queremos a±adir.
  534.  
  535. @Echo off
  536.  
  537. if "%1"=="" goto sin_args
  538.  
  539. :loop
  540.  
  541. if "%1"=="" goto fin_ok
  542.  
  543. if "%1"=="/A" goto permanente
  544.  
  545. if "%1"=="/a" goto permanente
  546.  
  547. set path=%path%;%1
  548.  
  549. shift
  550.  
  551. goto loop
  552.  
  553. :permanente
  554.  
  555. echo set path=%path% >> c:\autoexec.bat
  556.  
  557. goto fin
  558.  
  559. :sin_args
  560.  
  561. echo Sintaxis: MPATH camino [camino] ...
  562.  
  563. echo Para modificar AUTOEXEC.BAT: MPATH camino [camino] ... /A
  564.  
  565. goto final
  566.  
  567. :fin_ok
  568.  
  569. echo Nuevo PATH=%Path%
  570.  
  571. :final
  572.  
  573. Luis Rodrφguez Beteta
  574.  
  575. Madrid
  576.  
  577.  
  578.  
  579. CAPTURADOR DE PANTALLAS ASCII
  580.  
  581. El siguiente truco, realizado con Turbo Pascal, es un capturador de
  582. pantallas ASCII que ocupa menos de 10 Kbytes de memoria y cuyo
  583. funcionamiento se basa en que cuando se instala en memoria captura la
  584. interrupci≤n del teclado.
  585.  
  586. Para capturar una pantalla basta con pulsar la combinaci≤n ½Shift-F2╗,
  587. lo que darß lugar a que el programa genere un archivo llamado
  588. ½Capt-XX.txt╗, donde XX es sustituido por un 00, 01, 02... que
  589. equivale al n·mero de pantalla capturada.
  590.  
  591. {$M 2000, 0, 0}
  592.  
  593. {$R-, S-, I-, D+, F+,V-, V-, B-, N-, L-}
  594.  
  595. Program Capturador;
  596.  
  597. Uses Dos;
  598.  
  599. Const IntTec = $09;
  600.  
  601. Type Matriz = Array [1..2000] Of Byte;
  602.  
  603. Var
  604.  
  605.  VecTecAnt : Pointer;
  606.  
  607.  Buffer : Matriz;
  608.  
  609.  Archivo : File Of Matriz;
  610.  
  611.  XW1, XW2, XW3 : Word;
  612.  
  613.  XS1 : Strig;
  614.  
  615. Procedure CapturaPantalla;
  616.  
  617. Begin
  618.  
  619.  If Mem[Seg0040:$0049]=7 Then XW1:=$B000 Else XW1:=$B800;
  620.  
  621.  XW3:=1;
  622.  
  623.  For XW2:=0 To 3999 Do
  624.  
  625.  Begin
  626.  
  627.  Buffer[XW3]:=Mem[XW1:XW2];
  628.  
  629.  Inc(XW2);
  630.  
  631.  Inc(XW3);
  632.  
  633.  End;
  634.  
  635.  XW1:=0;
  636.  
  637.  Repeat
  638.  
  639.  Str(Xw1:2,XS1);
  640.  
  641.  If XS1[1]=#32 Then XS1[1]:='0';
  642.  
  643.  XS1:='CAPT-'+XS1+'.TXT';
  644.  
  645.  Assign (Archivo,XS1);
  646.  
  647.  Reset(Archivo);
  648.  
  649.  If IOResult = 0 The Close(Archivo)
  650.  
  651.  Else
  652.  
  653.  Begin
  654.  
  655.  Rewrite(Archivo);
  656.  
  657.  Write(Archivo, Buffer);
  658.  
  659.  Close(archivo);
  660.  
  661.  Exit;
  662.  
  663.  End;
  664.  
  665.  Inc(XW1);
  666.  
  667.  Until False;
  668.  
  669. End;
  670.  
  671. Procedure Teclado (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES,
  672. BP:Word); Interrupt;
  673.  
  674. Begin
  675.  
  676.  Asm
  677.  
  678.  pushf
  679.  
  680.  call VecTecAnt
  681.  
  682.  End;
  683.  
  684.  If Port[96]=60 Then CapturaPantalla;
  685.  
  686.  Inline($FB);
  687.  
  688. End;
  689.  
  690. Begin
  691.  
  692.  WriteLn('Capturador de pantallas ASCII');
  693.  
  694.  WriteLn('Para capturar la pantalla pulsar Shift-F2');
  695.  
  696.  GetIntVec(IntTec,VecTecAnt);
  697.  
  698.  SetIntVec(IntTec,@Teclado);
  699.  
  700.  Keep(0);
  701.  
  702. End.
  703.  
  704. Bernardo Calero Castellano
  705.  
  706. Tomelloso (Ciudad Real)
  707.  
  708. Nota del Laboratorio: La idea de esta utilidad consiste en visualizar
  709. un cursor en pantalla y que podamos moverlo y situarlo donde queramos.
  710. En este punto apretaremos ½ENTER╗ y podremos seguir moviendo el cursor
  711. para se±alar los dos vΘrtices de la zona a capturar. Una vez elegida
  712. la zona bastarß grabar el contenido de las posiciones de memoria que
  713. se encuentran entre los dos vΘrtices.
  714.  
  715.  
  716. MENSAJES POR VOZ
  717.  
  718. Muchas veces resulta interesante dejar una nota a la siguiente persona
  719. que va a utilizar el ordenador, o simplemente un recordatorio para
  720. nosotros mismos. Sin embargo, lo ideal serφa poder dejar un mensaje
  721. de viva voz. Pues bien, eso es lo que hace el programa
  722. ½mensajes.bat╗.
  723.  
  724. Para su correcto funcionamiento es imprescindible que se cumplan los
  725. siguientes requisitos:
  726.  
  727. 1. El programa estß ideado, en principio, para los poseedores de una
  728. tarjeta de sonido Sound Blaster, aunque tambiΘn se pueden dejar
  729. mensajes por escrito.
  730.  
  731. 2. Requiere la versi≤n 6.0 ≤ posteri≤r de MS-DOS (ya que utiliza el
  732. comando CHOICE).
  733.  
  734. 3. Se debe instalar el controlador ½ANSI.SYS╗ (o en caso contrario
  735. habrß que suprimir las secuencias de escape, ya que s≤lo sirven para
  736. cambiar el color de la pantalla).
  737.  
  738. 4. Los programas ½VPLAY╗ y ½VREC╗ de la Sond Blaster deben estar
  739. disponibles mediante la variable de entorno PATH. (TambiΘn se puede
  740. especificar la ruta de acceso cada vez que aparezca una llamada a
  741. cualquiera de estos dos programas)
  742.  
  743. 5. El programa debe ser instalado en un subdirectorio llamado
  744. ½mensajes╗.
  745.  
  746. Si ejecutamos la aplicaci≤n desde la lφnea de comandos se nos
  747. presentarß un men· desde el que podremos grabar mensajes escritos o
  748. hablados, escuchar el mensaje pregrabado o borrar el mensaje
  749. almacenado.
  750.  
  751. Si arrancamos la utilidad con la opci≤n r (½mensajes r╗) o incluyendo
  752. la llamada ½call c:\mensajes\mensajes.bat r╗ en el ½autoexec.bat╗, nos
  753. recordarß los mensajes hablados y/o escritos (si es que existen) y
  754. despuΘs nos darß la oportunidad de volver a escucharlos o borrarlos.
  755. En caso de que no exista ning·n mensaje esta llamada no tiene ning·n
  756. efecto.
  757.  
  758. @echo off
  759.  
  760. if "%1" =="r" goto principio
  761.  
  762. if "%1" =="R" goto principio
  763.  
  764. :principal
  765.  
  766. rem ** Rutina principal **
  767.  
  768. rem **********************
  769.  
  770. echo 
  771.  
  772. cls
  773.  
  774. echo  MENSAJES.BAT 
  775.  
  776. echo.
  777.  
  778. echo OPCIONES
  779.  
  780. echo.
  781.  
  782. echo [1] Grabar mensaje de texto
  783.  
  784. echo [2] Grabar mensaje de voz
  785.  
  786. echo.
  787.  
  788. echo [3] Escuchar mensaje
  789.  
  790. echo.
  791.  
  792. echo [4] Borrar mensaje
  793.  
  794. echo.
  795.  
  796. echo [0] SALIR
  797.  
  798. echo.
  799.  
  800. choice Opci≤n /c:01234
  801.  
  802. if errorlevel 5 goto borra
  803.  
  804. if errorlevel 4 goto principio
  805.  
  806. if errorlevel 3 goto grabavoz
  807.  
  808. if errorlevel 2 goto grabatexto
  809.  
  810. if errorlevel 1 goto end
  811.  
  812. :Grabavoz
  813.  
  814. cls
  815.  
  816. vrec mensaje /s:11000
  817.  
  818. goto principal
  819.  
  820. :Grabatexto
  821.  
  822. edit mensaje.txt
  823.  
  824. goto principal
  825.  
  826. rem ** Rutina que recuerda los mensajes y sale del programa **
  827.  
  828. rem **********************************************************
  829.  
  830. :principio
  831.  
  832. if exist c:\mensajes\mensaje.* goto rutina
  833.  
  834. echo MENSAJES.BAT: No hay mensajes para usted
  835.  
  836. echo.
  837.  
  838. if "%1" =="r" goto End
  839.  
  840. if "%1" =="R" goto End
  841.  
  842. goto principal
  843.  
  844. :rutina
  845.  
  846. echo 
  847.  
  848. cls
  849.  
  850. echo  MENSAJES.BAT 
  851.  
  852. echo Hay un mensaje para usted:
  853.  
  854. echo.
  855.  
  856. if not exist c:\mensajes\mensaje.txt goto voz
  857.  
  858. type c:\mensajes\mensaje.txt|more
  859.  
  860. :voz
  861.  
  862. echo 
  863.  
  864. if exist c:\mensajes\mensaje.voc vplay c:\mensajes\mensaje>nul
  865.  
  866. if exist c:\mensajes\mensaje.voc choice ┐Desea que le repita el
  867. mensaje
  868.  
  869. if not exist c:\mensajes\mensaje.voc pause
  870.  
  871. if errorlevel 2 goto sigue
  872.  
  873. if errorlevel 1 goto principio
  874.  
  875. :sigue
  876.  
  877. choice ┐Borrar mensaje
  878.  
  879. if errorlevel 2 goto continua
  880.  
  881. if errorlevel 1 goto borra
  882.  
  883. :Borra
  884.  
  885. echo Borrando mensaje... Espere por favor
  886.  
  887. del c:\mensajes\mensaje.txt>nul
  888.  
  889. del c:\mensajes\mensaje.voc>nul
  890.  
  891. if "%1" =="r" goto continua
  892.  
  893. if "%1" =="R" goto continua
  894.  
  895. goto principal
  896.  
  897. :continua
  898.  
  899. if "%1" =="r" goto End
  900.  
  901. if "%1" =="R" goto End
  902.  
  903. goto principal
  904.  
  905. :end
  906.  
  907. echo 
  908.  
  909. cls
  910.  
  911. Gorka Elexgaray
  912.  
  913. Guernica. (Vizcaya)
  914.  
  915.  
  916. PARTICION DE FICHEROS
  917.  
  918. En muchas ocasiones nos encontramos ante un fichero de texto cuyas
  919. dimensiones hacen imposible que sea editado. Mediante esta utilidad
  920. escrita en Turbo Pascal, podremos dividir cualquier fichero en
  921. diversos ficheros del numero de lφneas que deseemos. Por defecto se
  922. toma el n·mero de 5.000 lφneas por fichero.
  923.  
  924. {$i-}
  925.  
  926. Program Partidor;
  927.  
  928. Var
  929.  
  930.  Origen,
  931.  
  932.  Final :Text;
  933.  
  934.  No_de_lineas,
  935.  
  936.  Actual :LongInt;
  937.  
  938.  FichF :String[5];
  939.  
  940.  NoArch,
  941.  
  942.  Codigo :Integer;
  943.  
  944.  Item :String;
  945.  
  946.  No_fichero :Byte;
  947.  
  948. Begin
  949.  
  950.  No_de_lineas:=5000;
  951.  
  952.  If ParamCount>2 then Val(ParamStr(3), No_de_Lineas, Codigo);
  953.  
  954.  If (ParamCount<2) Or (Codigo<>0) then
  955.  
  956.  begin
  957.  
  958.  WriteLn('Usar: [unidad:][camino\]Partidor [unidad:]'+
  959.  
  960.  '[camino\]Arch_Original.ext Arch_final');
  961.  
  962.  Halt;
  963.  
  964.  End;
  965.  
  966.  FichF:=Copy(ParamStr(2),1,5);
  967.  
  968.  If No_de_lineas<512 then No_de_lineas:=512;
  969.  
  970.  Assign(Origen,ParamStr(1));
  971.  
  972.  Reset(Origen);
  973.  
  974.  If IOResult<>0 then
  975.  
  976.  begin
  977.  
  978.  WriteLn('Imposible abrir ',ParamStr(1));
  979.  
  980.  Halt(1);
  981.  
  982.  End;
  983.  
  984.  WriteLn('Fragmentando '+ParamStr(1)+'...');
  985.  
  986.  No_Fichero:=0;
  987.  
  988.  Repeat;
  989.  
  990.  Actual:=0;
  991.  
  992.  Inc(No_Fichero);
  993.  
  994.  Str(No_Fichero,Item);
  995.  
  996.  While Length(item)<3 do
  997.  
  998.  Item:='0'+Item;
  999.  
  1000.  Assign(Final,ParamStr(2)+'.'+Item);
  1001.  
  1002.  Rewrite(Final);
  1003.  
  1004.  If IOResult<>0 then
  1005.  
  1006.  begin
  1007.  
  1008.  WriteLn('Imposible crear '+ParamStr(2)+'.'+Item);
  1009.  
  1010.  Halt(1);
  1011.  
  1012.  End;
  1013.  
  1014.  While Not Eof(Origen) do
  1015.  
  1016.  begin
  1017.  
  1018.  ReadLn(Origen,Item);
  1019.  
  1020.  WriteLn(Final,Item);
  1021.  
  1022.  Inc(Actual);
  1023.  
  1024.  If Actual=No_de_lineas then break;
  1025.  
  1026.  End;
  1027.  
  1028.  Close(Final);
  1029.  
  1030.  Until Eof(Origen);
  1031.  
  1032.  Close(Origen);
  1033.  
  1034.  WriteLn('Archivo '+ParamStr(1)+' fragmentado en ',No_Fichero,'Archivos.');
  1035.  
  1036. End.
  1037.  
  1038. Los consejos del Laboratorio:
  1039.  
  1040. - Como ya hemos comentado, la utilidad de este programa radica en
  1041. poder dividir ficheros de texto que a causa de su tama±o no son
  1042. editables, en otros de menor tama±o. Una vez que hemos realizado los
  1043. cambios deseados sobre cada unos de los ficheros mßs peque±os, puede
  1044. que nos interese volver a juntar dichos archivos para tener de nuevo
  1045. un ·nico fichero. Para juntar todos los ficheros podemos recurrir a
  1046. la orden ½copy╗ del DOS, de la siguiente forma: ½copy
  1047. fich1+fich2+...+fichn fichfin╗
  1048.  
  1049. Luciano Rubio
  1050.  
  1051. Madrid
  1052.  
  1053.  
  1054. PARTICION DE FICHEROS MEJORADA
  1055.  
  1056. En el truco anterior hablßbamos de una utilidad que servφa para
  1057. descomponer ficheros de texto en otros de menor tama±o, para de esta
  1058. forma poder ser editados con facilidad. Esta nueva utilidad, aunque
  1059. bßsicamente hace lo mismo, tiene la ventaja de poder utilizarse tanto
  1060. con ficheros de texto como en ficheros binarios.
  1061.  
  1062. Su utilidad es clara, si tenemos un ejecutable que sobrepase la
  1063. capacidad de nuestros disquetes, podemos dividirlo en diferentes
  1064. ficheros binarios los cuales puedan ser grabados en disquetes, para
  1065. luego resconstruirlos a su formato original.
  1066.  
  1067. La sintaxis de uso la indica el propio programa, asφ como el tama±o y
  1068. numero de ficheros creados. De igual forma, indica como regenerar el
  1069. fichero original a partir de los trozos.
  1070.  
  1071. program trocear;
  1072.  
  1073.  uses dos,crt;
  1074.  
  1075. var
  1076.  
  1077.  fuente,destino:file of byte;
  1078.  
  1079.  i,veces,e:integer;
  1080.  
  1081.  tamano,c,tamanototal,j:longint;
  1082.  
  1083.  b:string[10];
  1084.  
  1085.  nombre,nombreb,nombreorigen:string[9];
  1086.  
  1087.  extension:string[3];
  1088.  
  1089.  letra:byte;
  1090.  
  1091.  fin:boolean;
  1092.  
  1093. begin
  1094.  
  1095.  fin:=false;
  1096.  
  1097.  if paramstr(1)='' then
  1098.  
  1099.  begin
  1100.  
  1101.  writeln('troceador por ram≤n bernardo 93');
  1102.  
  1103.  writeln;
  1104.  
  1105.  writeln('sintaxis: trocear fichero.ext tama╤o');
  1106.  
  1107.  writeln;
  1108.  
  1109.  writeln('donde tama±o estß entre 1024 y 2147483647 bytes');
  1110.  
  1111.  writeln;
  1112.  
  1113.  writeln('por defecto el tama±o es de 360 kb');
  1114.  
  1115.  halt(1);
  1116.  
  1117.  end
  1118.  
  1119.  else
  1120.  
  1121.  begin
  1122.  
  1123.  {$i+}
  1124.  
  1125.  nombreorigen:=paramstr(1);
  1126.  
  1127.  if paramstr(2)='' then b:='367616'
  1128.  
  1129.  else b:=paramstr(2);
  1130.  
  1131.  val(b,tamano,e);
  1132.  
  1133.  if tamano<1024 then
  1134.  
  1135.  begin
  1136.  
  1137.  writeln('error:el tama±o de los subficheros ha de ser mayor de 1024');
  1138.  
  1139.  halt(1);
  1140.  
  1141.  end;
  1142.  
  1143.  tamano:=1024*(tamano div 1024);
  1144.  
  1145.  assign(fuente,paramstr(1));
  1146.  
  1147.  reset(fuente);
  1148.  
  1149.  nombreb:='';
  1150.  
  1151.  nombre:='';
  1152.  
  1153.  for j:=1 to length(nombreorigen) do
  1154.  
  1155.  if nombreorigen[j]='.' then j:=length(nombreorigen)
  1156.  
  1157.  else nombreb:=concat(nombreb,nombreorigen[j]);
  1158.  
  1159.  veces:=0;
  1160.  
  1161.  repeat
  1162.  
  1163.  str(veces,extension);
  1164.  
  1165.  repeat
  1166.  
  1167.  extension:=concat('0',extension);
  1168.  
  1169.  until length(extension)=3;
  1170.  
  1171.  nombre:=concat(nombreb,'.',extension);
  1172.  
  1173.  write('creando el fichero ',nombre);
  1174.  
  1175.  c:=0;
  1176.  
  1177.  assign(destino,nombre);
  1178.  
  1179.  rewrite(destino);
  1180.  
  1181.  while (not eof(fuente) and (c < tamano)) do
  1182.  
  1183.  begin
  1184.  
  1185.  read(fuente,letra);
  1186.  
  1187.  write(destino,letra);
  1188.  
  1189.  c:=c+1;
  1190.  
  1191.  end;
  1192.  
  1193.  writeln(' ',c,' bytes');
  1194.  
  1195.  if eof(fuente) then fin:=true;
  1196.  
  1197.  close(destino);
  1198.  
  1199.  veces:=veces+1;
  1200.  
  1201.  c:=0;
  1202.  
  1203.  until fin;
  1204.  
  1205.  close(fuente);
  1206.  
  1207.  writeln;
  1208.  
  1209.  writeln;
  1210.  
  1211.  writeln('para regenerar el fichero original teclear :');
  1212.  
  1213.  writeln;
  1214.  
  1215.  writeln('si el fichero original es ascii (texto)');
  1216.  
  1217.  writeln('copy ',nombreb,'.000+',nombreb,'.001+',nombreb,'.002+... ',nombreb,'.extension');
  1218.  
  1219.  writeln;
  1220.  
  1221.  writeln('si el fichero original es binario (exe,com,..)');
  1222.  
  1223.  writeln('copy ',nombreb,'.000 /b+',nombreb,'.001 /b+',nombreb,'.002 /b+... ',nombreb,'.extension');
  1224.  
  1225.  end;
  1226.  
  1227.  {$i-}
  1228.  
  1229. end.
  1230.  
  1231. Ram≤n B. de Bernardo Hernßn
  1232.  
  1233. Sestao. Vizcaya
  1234.  
  1235.  
  1236. ORDENADOR PROTEGIDO
  1237.  
  1238. Mßs de una vez se habrß ausentado del ordenador durante unos minutos y
  1239. habrß querido bloquearlo para que nadie pudiera fisgar en Θl. O quizß
  1240. en alg·n momento dese≤ que desapareciera el contenido de la pantalla y
  1241. retornar a ella en cualquier momento. Este peque±o programa en C le
  1242. propone una soluci≤n.
  1243.  
  1244. Es una aplicaci≤n residente que se activa al pulsar una tecla o
  1245. combinaci≤n de ellas, salvando el contenido de la pantalla (modo
  1246. texto), borrandola y bloqueando el teclado. No se recuperarß la
  1247. pantalla inicial hasta que se pulsa la combinaci≤n de teclas elegida.
  1248. La rutina s≤lo se instala una vez, ya que si se vuelve a hacer es
  1249. ignorada.
  1250.  
  1251. Las teclas que se usan para activarlo son Ctrl+Shift (izquierda) y
  1252. para desactivarlo Alt+F1. Estas teclas pueden ser otras, a gusto del
  1253. usuario, con el ·nico problema de conocer el c≤digo correspondiente.
  1254. Para detectar la pulsaci≤n de Ctrl+Shift (izq) se busc≤ en el ßrea de
  1255. comunicaciones de la BIOS en la direcci≤n 0040:0017h (1 byte). Para
  1256. Alt+F1 se ha usado la interrupci≤n 16h de la BIOS, comprobando el
  1257. c≤digo de exploraci≤n y el c≤digo ASCII correspondiente (si existe).
  1258. Si el c≤digo ASCII no existe (es cero), se examina el c≤digo de
  1259. exploraci≤n.
  1260.  
  1261. En el caso de las teclas Alt+F1 el c≤digo ASCII correspondiente es 00,
  1262. mientras que el c≤digo de exploraci≤n es 68h.
  1263.  
  1264. #include <Dos.h>
  1265.  
  1266. #include <Bios.h>
  1267.  
  1268. #include <Stdio.h>
  1269.  
  1270. unsigned int far *mem;
  1271.  
  1272. void interrupt ( *oldhandler)();
  1273.  
  1274. /* Funci≤n a la que se asigna la interrupci≤n 9, que es la que
  1275. intercepta*/
  1276.  
  1277. void interrupt handler()
  1278.  
  1279. /* Funci≤n que reemplaza a la interrupci≤n 9*/
  1280.  
  1281. {
  1282.  
  1283.  char ah, p_activa;
  1284.  
  1285.  static char activo = 0; /* Variable semßforo */
  1286.  
  1287.  enable(); /*Habilitar interrupciones hardware */
  1288.  
  1289.  oldhandler();
  1290.  
  1291.  if ((peekb(0x40,0x17) & 6)==6 && activo==0)
  1292.  
  1293.  /*Crtl+Shift(Izq) Pulsadas*/
  1294.  
  1295.  {
  1296.  
  1297.  activo = 1;
  1298.  
  1299.  /* Si volvemos a pulsar Crtl+Shtf(Izq) no tendrß efecto */
  1300.  
  1301.  _AH=0xf; /* Se obtiene la pßgina de visualizaci≤n activa */
  1302.  
  1303.  geninterrupt (0x10);
  1304.  
  1305.  p_activa=_BH;
  1306.  
  1307.  _AH=0x5; /* Se cambia de pßgina activa */
  1308.  
  1309.  _AL=p_activa+3;
  1310.  
  1311.  geninterrupt (0x10);
  1312.  
  1313.  do
  1314.  
  1315.  {
  1316.  
  1317.  _AH=0; /* Se espera pulsaci≤n de tecla */
  1318.  
  1319.  geninterrupt (0x16);
  1320.  
  1321.  ah=_AH; /* Almacena c≤digo de exploraci≤n */
  1322.  
  1323.  }
  1324.  
  1325.  while(ah != 0x68); /* Mientras que no se pulse Alt+F1 */
  1326.  
  1327.  _AH=0x5; /* Se reestablece la pßgina activa inicial */
  1328.  
  1329.  _AL=p_activa;
  1330.  
  1331.  geninterrupt (0x10);
  1332.  
  1333.  activo=0;
  1334.  
  1335.  }
  1336.  
  1337. }
  1338.  
  1339. void main()
  1340.  
  1341. {
  1342.  
  1343.  /* Se comprueba que la rutina s≤lo se instale una vez */
  1344.  
  1345.  if( (FP_OFF(getvect(0x9))) != (FP_OFF(handler)) )
  1346.  
  1347.  {
  1348.  
  1349.  oldhandler=getvect(0x9);
  1350.  
  1351.  /* Guarda la direcci≤n de la interrupci≤n 9 que estß en la tabla de
  1352.  vectores de int */
  1353.  
  1354.  setvect(0x9,handler);
  1355.  
  1356.  /* Asigna la direcci≤n de nuestra rutina al vector de interrupci≤n
  1357.  de la int 9 */
  1358.  
  1359.  printf("*** Rutina instalada *** ");
  1360.  
  1361.  mem=MK_FP(_psp,0x2c);
  1362.  
  1363.  _AX=4900;
  1364.  
  1365.  _ES=*mem;
  1366.  
  1367.  geninterrupt(0x21); /*Elimina el bloque de entorno */
  1368.  
  1369.  keep(0,_SS + (_SP/16) - _PSP); /* Deja el programa residente */
  1370.  
  1371.  }
  1372.  
  1373. }
  1374.  
  1375. Los consejos del Laboratorio:
  1376.  
  1377. - Una forma de mejorar esta aplicaci≤n es introduciendo un peque±o
  1378. programa de configuraci≤n, de forma que al instalarse se pueda
  1379. modificar directamente las teclas que lo activan y lo desactivan.
  1380.  
  1381. - TambiΘn se le puede meter una rutina para que acepte un
  1382. parßmetro que haga que el programa se desinstale de la memoria.
  1383.  
  1384. Oscar Bernal Gonzßlez
  1385.  
  1386. Valladolid
  1387.  
  1388.  
  1389. LA LUZ DEL DISCO DURO
  1390.  
  1391. Mediante la siguiente rutina (escrita es Turbo Pascal 6.0) podemos
  1392. simular en pantalla el led de estado del disco duro. Esta simulaci≤n
  1393. puede representar grandes ventajas para aquellos usuarios que no
  1394. pueden visualizar dicho led, por tener la cpu debajo de la mesa, por
  1395. ejemplo.
  1396.  
  1397. program disklighter_v4;
  1398.  
  1399. {$m 1024,0,0}
  1400.  
  1401. (* reserva 1k para stack. *)
  1402.  
  1403. uses dos,crt;
  1404.  
  1405. type rutina_tratamiento=procedure;
  1406.  
  1407. const
  1408.  
  1409.  tiempo = 50;
  1410.  
  1411. var
  1412.  
  1413.  rutina_original :rutina_tratamiento;
  1414.  
  1415.  interrup_hd :byte;
  1416.  
  1417.  puntero,vector :pointer;
  1418.  
  1419. procedure nueva_rutina; interrupt;
  1420.  
  1421. var
  1422.  i :integer;
  1423.  
  1424. begin
  1425.  
  1426.  for i:=0 to tiempo do
  1427.  
  1428.  (* tiempo de luz. *)
  1429.  
  1430.  begin
  1431.  
  1432.  mem[$b800:156]:=ord('h');
  1433.  
  1434.  (* acceso a memoria de video. *)
  1435.  
  1436.  mem[$b800:157]:=$21;
  1437.  
  1438.  mem[$b800:158]:=ord('d');
  1439.  
  1440.  mem[$b800:159]:=$21;
  1441.  
  1442.  end;
  1443.  
  1444.  mem[$b800:157]:=$00;
  1445.  
  1446.  mem[$b800:159]:=$00;
  1447.  
  1448.  rutina_original;
  1449.  
  1450. end;
  1451.  
  1452. (* programa principal *)
  1453.  
  1454. begin
  1455.  
  1456.  writeln;
  1457.  
  1458.  writeln('disklighter v4.0 residente en memoria.');
  1459.  
  1460.  interrup_hd:=$76;
  1461.  
  1462.  getintvec(interrup_hd,vector);
  1463.  
  1464.  @rutina_original:=vector;
  1465.  
  1466.  setintvec(interrup_hd,@nueva_rutina);
  1467.  
  1468.  keep($00);
  1469.  
  1470. end.
  1471.  
  1472. Esta rutina intercepta la interrupci≤n que actua sobre el contador de
  1473. interrupciones 8259, la cual se genera cada vez que se accede al disco
  1474. duro, y redirecciona el vector que apunta a la rutina de tratamiento
  1475. de dicha interrupci≤n hacia esta nueva rutina, que visualiza en
  1476. pantalla ½una luz╗, y reenlaza despues con la rutina de tratamiento
  1477. original.
  1478.  
  1479. Seg·n la velocidad de la mßquina en la que se instale, deberß darse un
  1480. valor diferente a la constante ½Tiempo╗, por ejemplo, para un 386DX-25
  1481. un valor adecuado es 75, y para un 386DX-33 un valor adecuado es 210.
  1482. Si eligimos un valor de ½Tiempo╗ demasiado bajo, apenas veremos la
  1483. ½luz╗ y si se elige uno muiy alto el acceso al disco duro se
  1484. ralentiza. Es cuesti≤n de probar el valor ≤ptimo para nuestra
  1485. mßquina, teniendo en cuenta que cuanto mayor sea la velocidad del
  1486. ordenador, mayor deberß ser el valor que buscamos.
  1487.  
  1488. Luis Gasc≤n L≤pez
  1489.  
  1490. Valencia
  1491.  
  1492.  
  1493. LA FECHA DEL SISTEMA
  1494.  
  1495. En ocasiones puede ser ·til la visualizaci≤n de la fecha del dφa, pero
  1496. la instrucci≤n ½Time╗ del DOS no es lo mßs adecuado para ello. Este
  1497. peque±o programa en C, nos permite la visualizaci≤n de la fecha en su
  1498. formato correcto.
  1499.  
  1500. #include <stdio.h>
  1501.  
  1502. #include <dos.h>
  1503.  
  1504. #include <time.h>
  1505.  
  1506. void main()
  1507.  
  1508. {
  1509.  
  1510.  char *day[]={"Domingo",
  1511.  
  1512.  "Lunes",
  1513.  
  1514.  "Martes",
  1515.  
  1516.  "MiΘrcoles",
  1517.  
  1518.  "Jueves",
  1519.  
  1520.  "Viernes",
  1521.  
  1522.  "Sßbado"};
  1523.  
  1524.  static char *meses[]={"Enero",
  1525.  
  1526.  "Febrero",
  1527.  
  1528.  "Marzo",
  1529.  
  1530.  "Abril",
  1531.  
  1532.  "Mayo",
  1533.  
  1534.  "Junio",
  1535.  
  1536.  "Julio",
  1537.  
  1538.  "Agosto",
  1539.  
  1540.  "Septiembre",
  1541.  
  1542.  "Octubre",
  1543.  
  1544.  "Noviembre",
  1545.  
  1546.  "Diciembre",
  1547.  
  1548.  };
  1549.  
  1550.  time_t time_t;
  1551.  
  1552.  struct date date;
  1553.  
  1554.  struct tm*tm;
  1555.  
  1556.  getdate(&date);
  1557.  
  1558.  time_t=dostounix(&date,NULL);
  1559.  
  1560.  tm=localtime(&time_t);
  1561.  
  1562.  gotoxy(45,2);
  1563.  
  1564.  cprintf(" %s ",day[tm-> tm_wday]);
  1565.  
  1566.  cprintf("%02d",date.da_day);
  1567.  
  1568.  cprintf(" de %s",meses[tm-> tm_mon]);
  1569.  
  1570.  cprintf(" de %d\n",date.da_year);
  1571.  
  1572. }
  1573.  
  1574. Nota de la Laboratorio TΘcnico:
  1575.  
  1576. La fecha se visualiza en el estremo superior derecho de la pantalla,
  1577. para cambiar esto basta con modificar las coordenadas que aparecen en la
  1578. instrucci≤n ½gotoxy╗.
  1579.  
  1580. Jose Angel Vidal Gallego
  1581.  
  1582. Alacuas. Valencia
  1583.  
  1584.  
  1585. CRONOMETRO DE PROGRAMAS
  1586.  
  1587. El siguiente programa realizado en C, tiene la utilidad de cronometrar
  1588. el tiempo que estß un programa funcionando. Fue mandado por su autor
  1589. como una aplicaci≤n para medir el tiempo que empleaba su programa en
  1590. escapar del laberento, dentro del concurso de programaci≤n, pero por
  1591. su utilidad lo hemos incluido dentro de esta secci≤n.
  1592.  
  1593. #include <stdio.h>
  1594.  
  1595. #include <stdlib.h>
  1596.  
  1597. #include <conio.h>
  1598.  
  1599. #include <process.h>
  1600.  
  1601. #include <time.h>
  1602.  
  1603. void main()
  1604.  
  1605. {
  1606.  char prog[80];
  1607.  
  1608.  int r;
  1609.  
  1610.  clock_t antes, despues;
  1611.  
  1612.  double segs;
  1613.  
  1614.  printf( "Programa a cronometrar ? " );
  1615.  
  1616.  gets( prog );
  1617.  
  1618.  antes = clock();
  1619.  
  1620.  r = _spawnlp( _P_WAIT, prog, prog, NULL );
  1621.  
  1622.  despues = clock();
  1623.  
  1624.  segs = (double)(despues - antes) / CLOCKS_PER_SEC;
  1625.  
  1626.  if( r == -1 )
  1627.  
  1628.  printf( "\nNo se pudo ejecutar :\n%s" , prog );
  1629.  
  1630.  else
  1631.  
  1632.  printf( "\n%f segundos tarda en ejecutar :\n%s" , segs , prog );
  1633.  
  1634.  exit( r );
  1635.  
  1636. }
  1637.  
  1638. Joaquφn Obreg≤n Cobo
  1639.  
  1640. El Astillero. Cantabria
  1641.  
  1642.  
  1643. LA CLAVE DE LAS BIOS AMI
  1644.  
  1645. Aunque es una situaci≤n muy poco frecuente, algunos de nuestros
  1646. lectores se han encontrado con la desagradable sorpresa de que al
  1647. adquirir un ordenador con BIOS AMI (nuevo o de segunda mano) a
  1648. personas poco fiables, el setup del mismo se encontraba protegido con
  1649. una palabra clave que el vendedor se resistφa a revelar. De esta
  1650. forma, algunas empresas se aseguran de que el usuario s≤lo podrß
  1651. modificar el hardware de su PC bajo su conocimiento, perdiendo la
  1652. garantφa o bien pagando una cantidad extra por la instalaci≤n.
  1653.  
  1654. Tanto para este caso extremo como si simplemente se nos olvida nuestra
  1655. propia clave, puede resultar de utilidad la siguiente rutina, que
  1656. sirve para averiguar la password de las BIOS AMI. Para ello crearemos
  1657. en primer lugar un fichero llamado ½crackami.dbg╗ con el siguiente
  1658. contenido:
  1659.  
  1660. a
  1661.  
  1662. jmp 118
  1663.  
  1664. db 37
  1665.  
  1666. mov al,[102]
  1667.  
  1668. out 70,al
  1669.  
  1670. jmp 10a
  1671.  
  1672. inc byte ptr [102]
  1673.  
  1674. cmp byte ptr [102],3f
  1675.  
  1676. je 13f
  1677.  
  1678. in al,71
  1679.  
  1680. ret
  1681.  
  1682. call 103
  1683.  
  1684. and al,f0
  1685.  
  1686. mov ah,al
  1687.  
  1688. call 103
  1689.  
  1690. or al,al
  1691.  
  1692. jz 13f
  1693.  
  1694. push ax
  1695.  
  1696. xor cl,cl
  1697.  
  1698. cmp al,ah
  1699.  
  1700. je 138
  1701.  
  1702. test al,e1
  1703.  
  1704. jpe 132
  1705.  
  1706. stc
  1707.  
  1708. rcl al,1
  1709.  
  1710. inc cl
  1711.  
  1712. jnz 129
  1713.  
  1714. mov al,cl
  1715.  
  1716. int 29
  1717.  
  1718. pop ax
  1719.  
  1720. jmp 11d
  1721.  
  1722. xor al,al
  1723.  
  1724. ret
  1725.  
  1726. db '// 1.1 // ARM '
  1727.  
  1728. <lφnea en blanco>
  1729.  
  1730. rcx
  1731.  
  1732. 50
  1733.  
  1734. n crackami.com
  1735.  
  1736. w
  1737.  
  1738. q
  1739.  
  1740. <lφnea en blanco>
  1741.  
  1742. A continuaci≤n damos la orden ½DEBUG <crackami.dbg╗ para generar el
  1743. fichero ejecutable. Al lanzar el programa ½CRACKAMI.COM╗ asφ creado
  1744. se imprimirß en pantalla la palabra clave almacenada actualmente en la
  1745. memoria CMOS de nuestro ordenador.
  1746.  
  1747. Antes de finalizar, hemos de hacer dos serias advertencias. La
  1748. primera es que AMI ha modificado el mΘtodo de encriptaci≤n de la clave
  1749. en sus BIOS mßs recientes, las de 1994 (y en algunas del 93), de modo
  1750. que en estos casos no funcionarß la presente rutina. La segunda y mßs
  1751. importante es que este programa s≤lo puede ser utilizado con el propio
  1752. ordenador: es un delito emplearlo en otra mßquina sin autorizaci≤n
  1753. expresa de su due±o, con el fin de tener acceso a la informaci≤n
  1754. almacenada.
  1755.  
  1756. Arturo Ramφrez-Montesinos Krogulski
  1757.  
  1758. Boadilla del Monte (Madrid)
  1759.  
  1760. Nota: Si el programa es incapaz de averiguar la clave almacenada en
  1761. la CMOS, habremos de recurrir a un mΘtodo ½artesanal╗. Para ello
  1762. llevaremos a cabo lo que se conoce como cortocircuitar la placa base,
  1763. uniendo mediante un cable las dos patillas de la baterφa de la CMOS.
  1764. Esto tiene como efecto inmediato la descarga total de todos los datos
  1765. almacenados en la propia CMOS (parßmetros de disco duro, memoria y
  1766. disqueteras incluidas). En ciertas placas base basta con variar la
  1767. posici≤n de un jumper para llevar a cabo esta descarga.
  1768.  
  1769.  
  1770. CALLAR A LA SOUND BLASTER
  1771.  
  1772. Este truco estß dirigido a quienes posean una Sound Blaster y cuyo
  1773. sonido hubieran deseado anular en alguna ocasi≤n sin tener que
  1774. recurrir al potenci≤metro del amplificador. La siguiente rutina,
  1775. escrita en Borland C++ 2.0, permite reducir el volumen de dicha
  1776. tarjeta usando la tecla Scroll Lock (o Bloq Despl).
  1777.  
  1778. El funcionamiento del programa es muy sencillo. En primer lugar se
  1779. accede al chip del mezclador (puerto 224h) y se selecciona el registro
  1780. n·mero 3, que es el que controla el volumen de los dos altavoces de la
  1781. Sound Blaster. Luego, mediante el puerto de datos del chip, se reduce
  1782. el volumen hasta cero o hasta ½Ah╗ (este valor se puede cambiar a
  1783. gusto del usuario) seg·n el estado de la tecla Bloq Despl. Tras ello
  1784. se deja el programa residente en memoria.
  1785.  
  1786. El puerto base de la Sound Blaster que se utiliza en este programa es
  1787. el 220h. Pero hay algunas tarjetas que utilizan el puerto 240h. En
  1788. este caso, se deberß modificar el valor de la variable ½BASE╗.
  1789.  
  1790. #include "dos.h"
  1791.  
  1792. void interrupt (*oldkbd)();
  1793.  
  1794. void interrupt handler();
  1795.  
  1796. void interrupt handler()
  1797.  
  1798. {
  1799.  
  1800.  int tecla
  1801.  
  1802.  unsigned int BASE = 0x220;
  1803.  
  1804.  //Puerto base por defecto de la SB
  1805.  
  1806.  unsigned int CHIPBASE = BASE + 4;
  1807.  
  1808.  tecla = peek (0x0040, 0x0017);
  1809.  
  1810.  // Lee el estado del teclado
  1811.  
  1812.  if (tecla & 16)
  1813.  
  1814.  // Si la tecla Bloq Despl estß encendida
  1815.  
  1816.  {
  1817.  
  1818.  asm {
  1819.  
  1820.  mov dx, CHIP
  1821.  
  1822.  // Puerto de registros del chip mezclador
  1823.  
  1824.  mov al, 3 // Volumen Master
  1825.  
  1826.  out dx, al // Seleccionar registro
  1827.  
  1828.  inc dx
  1829.  
  1830.  xor al, al // Reduce volumen
  1831.  
  1832.  out dx, al
  1833.  
  1834.  }
  1835.  
  1836.  }
  1837.  
  1838.  Else
  1839.  
  1840.  // Si estß apagada...
  1841.  
  1842.  {
  1843.  
  1844.  asm {
  1845.  
  1846.  mov dx, CHIP
  1847.  
  1848.  mov al, 3
  1849.  
  1850.  out dx, al
  1851.  
  1852.  inc dx // Puerto de datos del chip mezclador
  1853.  
  1854.  xor al, 0ah // Aumento el volumen
  1855.  
  1856.  out dx, al
  1857.  
  1858.  }
  1859.  
  1860.  }
  1861.  
  1862.  oldkbd (); // Llamo a la antigua interrupci≤n
  1863.  
  1864. }
  1865.  
  1866. void main (int argc, char **argv)
  1867.  
  1868. {
  1869.  
  1870.  oldkbd=getvect(0x9);
  1871.  
  1872.  setvect(0x9, handler);
  1873.  
  1874.  printf("\nSound Blaster PRO Volume");
  1875.  
  1876.  printf("\n========================");
  1877.  
  1878.  printf("\n Programa en memoria.");
  1879.  
  1880.  printf("\n========================\n");
  1881.  
  1882.  keep(0, (_SS+(_SP/16)- _psp)); //Instala el programa
  1883.  
  1884. }
  1885.  
  1886.  
  1887. Eduard Sßnchez Palaz≤n
  1888.  
  1889. Terrassa (Barcelona)
  1890.  
  1891.  
  1892. El UTIL XCOPY
  1893.  
  1894. Muchas veces se nos ha presentado el problema de tener que copiar del
  1895. disco duro a una unidad de disquete, sobrepasando con lo que deseamos
  1896. copiar el espacio del disco, teniendo que recurrir a varios disquetes
  1897. y efectuar la copia fichero a fichero para evitar la tan temida frase
  1898. de ½espacio insuficiente╗.
  1899.  
  1900. Para remediar este problema, el mismo sistema operativo nos ofrece una
  1901. soluci≤n:
  1902.  
  1903. Primero ponemos el atributo de archivo a todos los ficheros que
  1904. deseemos copiar a disquete:
  1905.  
  1906. Por ejemplo: ½Attrib c:\graficos\*.* +A╗
  1907.  
  1908. Si queremos copiar tambiΘn los subdirectorios podemos a±adir el
  1909. parßmetro /s
  1910.  
  1911. Introducir el disco en la unidad destino y escribir la orden de copia.
  1912. Por ejemplo:
  1913.  
  1914. ½xcopy c:\graficos\*.* /m╗
  1915.  
  1916. Con el parßmetro /m conseguimos que se vaya modificando el atributo de
  1917. archivo segun se copian los diferentes ficheros.
  1918.  
  1919. Ejecutaremos este ·ltimo paso y el ordenador empezarß a copiar hasta
  1920. que no haya mßs espacio en el disco destino; entonces mostrarß el
  1921. mensaje de ½espacio insuficiente en disco╗ en cuyo caso sustituiremos
  1922. el disquete y repetiremos de nuevo el ·ltimo paso, con lo que
  1923. continuarß copiando en el archivo en que se qued≤. Si repetimos este
  1924. paso cuantas veces sea necesario habremos copiado todos los ficheros
  1925. que deseabamos.
  1926.  
  1927. Joaquφn Dionisio Sßnchez
  1928.  
  1929. Mßlaga
  1930.  
  1931.  
  1932. INTERRUPCIONES CON QUICKBASIC
  1933.  
  1934. El uso de interrupciones a la hora de realizar cualquier programa es
  1935. una buena tΘcnica para aumentar el grado de profesionalidad en Θstos,
  1936. ya que asφ podremos emplear servicios de la ROM-BIOS y del DOS tales
  1937. como incluir soporte para rat≤n, controlar el subsistema grßfico, etc.
  1938.  
  1939. Como sabΘis, el uso de interrupciones resulta mßs fßcil de llevar a la
  1940. prßctica en unos lenguajes que en otros, aunque no siempre se deba a
  1941. limitaciones del propio lenguaje. En el caso de QuickBasic 4.5 parece
  1942. que la causa reside mßs bien en la falta de documentaci≤n acerca de
  1943. c≤mo acceder a este tipo de servicios.
  1944.  
  1945. Desde tierras levantinas nos ha llegado la soluci≤n al problema, de
  1946. modo que quienes deseen utilizar las interrupciones en sus programas
  1947. simplemente tendrßn que seguir los pasos que a continuaci≤n
  1948. describimos. En primer lugar, hay que crear un archivo ASCII, llamado
  1949. ½intxs.bi╗, con el siguiente contenido:
  1950.  
  1951. TYPE tReg
  1952.  
  1953.  ax AS INTEGER
  1954.  
  1955.  bx AS INTEGER
  1956.  
  1957.  cx AS INTEGER
  1958.  
  1959.  dx AS INTEGER
  1960.  
  1961.  bp AS INTEGER
  1962.  
  1963.  si AS INTEGER
  1964.  
  1965.  di AS INTEGER
  1966.  
  1967.  flags AS INTEGER
  1968.  
  1969.  ds AS INTEGER
  1970.  
  1971.  es AS INTEGER
  1972.  
  1973. END TYPE
  1974.  
  1975. DIM SHARED rEn AS tReg
  1976.  
  1977. DIM SHARED rFue AS tReg
  1978.  
  1979. DECLARE SUB INTERRUPTX(num AS INTEGER, rEn AS tReg, rFue AS tReg)
  1980.  
  1981. A continuaci≤n, iniciaremos QuickBasic con el parßmetro ½/l╗ (por
  1982. ejemplo, ½C:\QB45\QB /l╗).
  1983.  
  1984. Al principio del programa en el que queramos acceder a alguna
  1985. interrupci≤n a±adiremos la orden ½$INCLUDE: 'intxs.bi'╗. (El archivo
  1986. ½intxs.bi╗ debe estar en el subdirectorio donde estΘ el QuickBasic).
  1987.  
  1988. Para realizar una interrupci≤n desde cualquier punto del programa
  1989. basta con ejecutar la orden ½CALL INTERRUPTX(num, rEn, rFue)╗, siendo
  1990. ½num╗ el n·mero del servicio que queremos usar.
  1991.  
  1992. Por lo general, tendremos que ½pasar╗ unos parßmetros para indicar quΘ
  1993. funci≤n queremos usar del servicio y otros datos. En ciertas
  1994. ocasiones tambiΘn serß necesario obtener el resultado de la llamada al
  1995. servicio. Los parßmetros de entrada se tienen que dejar en la
  1996. variable ½rEn╗, mientras que los de salida se obtienen o recogen en
  1997. ½rFue╗.
  1998.  
  1999. A continuaci≤n presentamos un esquema general:
  2000.  
  2001.  
  2002. rem parßmetros de entrada (si son necesarios)
  2003.  
  2004. rEn.ax = &Hxxxx
  2005.  
  2006. rEn.bx = &Hyyyy
  2007.  
  2008. rEn.cx = &Hzzzz
  2009.  
  2010. rem llamada al servicio
  2011.  
  2012. CALL INTERRUPTX(&Hxx,rEn,rFue)
  2013.  
  2014. rem evaluaci≤n de la salida (si es necesario)
  2015.  
  2016. IF rFue.ax=nn THEN <acciones>
  2017.  
  2018. PRINT rFue.bx
  2019.  
  2020.  
  2021. Hay que se±alar que el mΘtodo tiene el inconveniente de la
  2022. imposibilidad de separar los registros AX, BX, CX y DX en sus bytes
  2023. respectivos (AH, AL; BH, BL;...), por lo que nos veremos obligados a
  2024. trabajar con el registro entero. TambiΘn hay que advertir que la
  2025. forma mßs c≤moda de trabajar es en hexadecimal.
  2026.  
  2027. Veamos a continuaci≤n c≤mo utilizarlo. Supongamos que tenemos que
  2028. pasar al servicio los siguientes parßmetros de entrada: ½AL = x╗ y
  2029. ½AH = y╗.
  2030.  
  2031. En primer lugar convertimos ½x╗ e ½y╗ a hexadecimal:
  2032.  
  2033. ½x╗ lo convertimos en &Hmm, e ½y╗ lo convertimos &Hnn
  2034.  
  2035. (de forma que ½mm╗ y ½nn╗ sean de 2 dφgitos, rellenando con un ½0╗ por
  2036. la izquierda si es necesario).
  2037.  
  2038. Los parßmetros de entrada serßn ½rEn = &Hnnmm╗.
  2039.  
  2040. Si alg·n byte no hay que usarlo lo llenamos con ½00╗. Es decir, si
  2041. por ejemplo tenemos que pasarle en BL el valor 10, la instrucci≤n serß
  2042. ½rEn.bx=&H000A╗.
  2043.  
  2044. De igual forma, a la hora de obtener los resultados en ½rFue╗ habrß
  2045. que separar en AH, AL; BH, BL; ... El procedimiento para ello es el
  2046. siguiente: convertimos el registro a separar (por ejemplo BX) a
  2047. hexadecimal; obtendremos entonces 4 dφgitos (si son menos se rellena
  2048. por la izquierda con ½0╗ hasta que sean 4). Los dos primeros, por la
  2049. derecha, corresponderßn a BH y los dos siguientes a BL. Una vez
  2050. separados los podemos transformar a su vez en decimal o binario seg·n
  2051. nuestras necesidades.
  2052.  
  2053. Sergio Ballester Lorente
  2054.  
  2055. Godella. (Valencia)
  2056.  
  2057.  
  2058. ALARMA RESIDENTE
  2059.  
  2060. Delante del ordenador las horas pasan muy rßpidamente y por eso
  2061. es aconsejable disponer de un reloj con alarma a mano. íQuΘ mejor
  2062. reloj que el interno del ordenador! Este programa es una alarma que
  2063. avisarß de la hora indicada con unos pitidos. Se queda residente y
  2064. emplea muy poca memoria para ello. Estß realizado en ensamblador y
  2065. podemos emplear el ½debug╗ para compilarlo.
  2066.  
  2067. a
  2068.  
  2069. mov ax,2528
  2070.  
  2071. mov dx,0117
  2072.  
  2073. int 21
  2074.  
  2075. mov ah,49
  2076.  
  2077. mov es,[2c]
  2078.  
  2079. int 21
  2080.  
  2081. mov ah,31
  2082.  
  2083. mov dx,14
  2084.  
  2085. int 21
  2086.  
  2087. nop
  2088.  
  2089. nop
  2090.  
  2091. push ax
  2092.  
  2093. push bx
  2094.  
  2095. push cx
  2096.  
  2097. push dx
  2098.  
  2099. mov ah,02
  2100.  
  2101. int 1a
  2102.  
  2103. cmp ch,05 ;Horas
  2104.  
  2105. jne 139
  2106.  
  2107. cmp cl,05 ;minutos
  2108.  
  2109. jne 139
  2110.  
  2111. cmp dh,02 ;segundos
  2112.  
  2113. jne 139
  2114.  
  2115. mov cx,04
  2116.  
  2117. mov al,07
  2118.  
  2119. mov ah,0e
  2120.  
  2121. int 10
  2122.  
  2123. pop dx
  2124.  
  2125. pop cx
  2126.  
  2127. pop bx
  2128.  
  2129. pop ax
  2130.  
  2131. iret
  2132.  
  2133. <Lφnea en blanco>
  2134.  
  2135. rcx
  2136.  
  2137. 3e
  2138.  
  2139. n alarma.com
  2140.  
  2141. w
  2142.  
  2143. q
  2144.  
  2145.  
  2146. Gontzal Uriarte G≤mez
  2147.  
  2148. San Juan (Alicante)
  2149.  
  2150.  
  2151. FICHEROS ½BATCH╗
  2152.  
  2153. Muchos usuarios habrßn necesitado en mßs de una ocasi≤n que un fichero
  2154. batch envφe su salida a la impresora o a un fichero de texto, y
  2155. l≤gicamente habrßn hecho lo siguiente:
  2156.  
  2157. Fbatch > Salida.prn
  2158.  
  2159. Suponiendo que el fichero se llama ½Fbatch.bat╗ y se desea volcar la
  2160. salida al fichero ½Salida.prn╗.
  2161.  
  2162. Pero el funcionamiento interno del DOS no lo permite (al contrario que
  2163. en otros sistemas operativos como Unix), ya que s≤lo se redirecciona
  2164. la salida de programas ½.COM╗ o ½.EXE╗ que utilicen la salida
  2165. estßndar.
  2166.  
  2167. La soluci≤n estß en emular el comportamiento de sistemas operativos
  2168. como el Unix. Veamos quΘ hace el Unix cuando lanza un fichero script
  2169. (batch para MS-DOS). Lo primero que hace es cargar un nuevo shell que
  2170. ejecute los comandos del fichero y redireccionar la salida donde se
  2171. haya especificado o a la consola por defecto. Pues bien, esto
  2172. traducido en MS-DOS serφa:
  2173.  
  2174. Command /C Fbatch > Salida.prn
  2175.  
  2176. En realidad se le estß diciendo que se cargue un nuevo shell
  2177. (½command.com╗) y se redireccione la salida estßndar de Θste al
  2178. fichero ½Salida.prn╗, pero que ·nicamente se ejecuten los comandos que
  2179. hay en ½Fbatch.bat╗ y regrese al shell anterior (parßmetro /C).
  2180.  
  2181. Gabriel Martφ
  2182.  
  2183. Barcelona
  2184.  
  2185.  
  2186.  
  2187.  
  2188.  
  2189.  
  2190.  
  2191.